package earogya.ehr.medical_records.messages;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.DataTypeException;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.model.v251.datatype.CE;
import ca.uhn.hl7v2.model.v251.datatype.PL;
import ca.uhn.hl7v2.model.v251.datatype.ST;
import ca.uhn.hl7v2.model.v251.datatype.TS;
import ca.uhn.hl7v2.model.v251.datatype.XAD;
import ca.uhn.hl7v2.model.v251.datatype.XPN;
import ca.uhn.hl7v2.model.v251.datatype.XTN;
import ca.uhn.hl7v2.model.v251.message.ADT_A02;
import ca.uhn.hl7v2.model.v251.segment.DG1;
import ca.uhn.hl7v2.model.v251.segment.EVN;
import ca.uhn.hl7v2.model.v251.segment.MSH;
import ca.uhn.hl7v2.model.v251.segment.NK1;
import ca.uhn.hl7v2.model.v251.segment.OBX;
import ca.uhn.hl7v2.model.v251.segment.PID;
import ca.uhn.hl7v2.model.v251.segment.PV1;
import earogya.ehr.medical_records.*;
import earogya.util.*;

import org.hibernate.HibernateException;
import org.hibernate.SQLQuery;
import org.hibernate.Session;

/**
 * An A02 event is issued as a result of the patient changing his or her assigned physical location.
 * The new patient location should appear in PV1-3-assigned 
 * patient location while the old patient location should appear 
 * in PV1-6-prior patient location.  
 * For example, an A02 event can be used to notify:  laboratory, radiology, pathology that the patient has changed location 
 * and test results should be redirected; 
 * pharmacy that drugs should be redirected for the patient; 
 * dietary that the meals should be delivered to a different location; 
 * the clinical repository that a transfer has taken place for the EMR.  
 * @author Chamendri
 *
 */
public class ADT_02_Handler {

	private ADT_A02 adtMsg;
	private Admission admission=new Admission();
	private Guardian g=new Guardian();
	private Bht bht=new Bht();
	private HibernateManager hibernate_manager=new HibernateManager();
	private TransferToAnotherWard transfer=new TransferToAnotherWard();
	private TransferToAnotherHospital transfer_to_another_hospital=new TransferToAnotherHospital();
	private BedExchange exchange=new BedExchange();
	
	public ADT_02_Handler(ADT_A02 adtMsg){
		this.adtMsg=adtMsg;
	}
	
	
	/**
	 * Identify separate segments in the message and add the details in the
	 * segments to their respective POJO classes
	 * @return - the acknowledgement of the HL7 message
	 * @throws HL7Exception
	 * @throws IOException
	 * @throws HL7Exception 
	 */
	public String manageMessage() throws HibernateException, IOException, HL7Exception{
		String ack=null;
		try{
			PV1 pv1=adtMsg.getPV1();
			String fromHospital=pv1.getPriorPatientLocation().getFacility().getUniversalID().getValue();
			if(pv1.getAssignedPatientLocation().getFacility().getUniversalID().getValue()==null||pv1.getAssignedPatientLocation().getFacility().getUniversalID().getValue().equals(fromHospital)){
				this.transferToAnotherWard();
				this.updateBht(transfer.getAdmissionId(), Integer.parseInt(transfer.getToWardNo()), Integer.parseInt(transfer.getToBedNo()));
			}
			else{
				this.transferToAnotherHospital();
			}
			ack=adtMsg.generateACK().encode();
		
		}catch(HL7Exception e){
			ack=adtMsg.generateACK("AE", e).encode();
		}catch(HibernateException e){
			HL7Exception ex=new HL7Exception(e.getMessage());
			ack=adtMsg.generateACK("AR", ex).encode();
		}catch(IOException e){
			HL7Exception ex=new HL7Exception(e.getMessage());
			ack=adtMsg.generateACK("AR", ex).encode();
		}
		return ack;
	}
	
	
	 public void transferToAnotherWard() throws HL7Exception{
		 System.out.print("Transfer to another ward");
		 PV1 pv1=adtMsg.getPV1();
		 PL fromPlace=pv1.getPriorPatientLocation();
		 System.out.print(adtMsg.getPID().getPatientID().getIDNumber().getValue());
		 int admission_id=this.getAdmissionId();
		 String from_bed=fromPlace.getBed().getValue();
		 String from_ward=fromPlace.getRoom().getValue();
		 String to_bed=pv1.getAssignedPatientLocation().getBed().getValue();
		 String to_ward=pv1.getAssignedPatientLocation().getRoom().getValue();
		 if(isBedOccupiedByCorrectPerson(Integer.parseInt(from_bed), Integer.parseInt(from_ward), admission_id)){
			 if(!isBedOccipued(Integer.parseInt(to_bed), Integer.parseInt(to_ward))){
				 this.transfer.setPatientId(Integer.parseInt(adtMsg.getPID().getPatientID().getIDNumber().getValue()));
				 this.transfer.setAdmissionId(admission_id);
				 this.transfer.setHospital(fromPlace.getFacility().getUniversalID().getValue());
				 this.transfer.setFromWardNo(from_ward);
				 this.transfer.setFromBedNo(from_bed);
				 this.transfer.setToWardNo(to_ward);
				 this.transfer.setToBedNo(to_bed);
				 this.transfer.setTreatmentSuggested(adtMsg.getPV2().getPatientConditionCode().getAlternateText().getValue());
				 this.transfer.setTransferringOfficerId(pv1.getConsultingDoctor(0).getIDNumber().getValue());
				 this.transfer.setReason(adtMsg.getPV2().getTransferReason().getText().getValue());
				 this.transfer.setTransferTime(pv1.getDischargeDateTime(0).getTime().getValueAsDate());
				 Boolean isSuccessful=hibernate_manager.saveObject(transfer);
				 if(isSuccessful){
					 exchange.setAdmission_id(admission_id);
					 exchange.setCurrent_bed(Integer.parseInt(to_bed));
					 exchange.setCurrent_ward(Integer.parseInt(to_ward));
					 exchange.setExchange_date(pv1.getDischargeDateTime(0).getTime().getValueAsDate());
					 exchange.setPatient_id(Integer.parseInt(adtMsg.getPID().getPatientID().getIDNumber().getValue()));
					 exchange.setPrevious_bed(Integer.parseInt(from_bed));
					 exchange.setPrevious_ward(Integer.parseInt(from_ward));
					 hibernate_manager.saveObject(exchange);
				 }
				 else{
					 HL7Exception ex=new HL7Exception("Error while transfering patient!");
					 throw ex;
				 }
			 }
			 else{
				 HL7Exception ex=new HL7Exception("Destination bed is already occupied");
				 throw ex;
			 }
		 }
		 else{
			 HL7Exception ex=new HL7Exception("Given patient does not occupy the given bed");
			 throw ex;
		 }
	 }
	 
	 private Boolean updateBht(int admission_id, int ward, int bed) throws HibernateException{
			Boolean isSucessful=false;
			Object[][] fieldsAndValues={{"admission_id", admission_id}};
			int bht_id=hibernate_manager.queryValuesFromDatabase(fieldsAndValues, "bht_id", "Bht", "bht");
			if(bht_id!=-1){
				isSucessful=hibernate_manager.updateBhtEntry(bht_id, bed, ward);
			}
			return isSucessful;
		}
	 
	 private int getAdmissionId() throws NumberFormatException, DataTypeException{
		int admissionId=0;
		PID pid=adtMsg.getPID();
		System.out.print(pid.getPatientID().getIDNumber().getValue());
		
		int patientId=Integer.parseInt(pid.getPatientID().getIDNumber().getValue());
		Object[][] fieldsAndValues={{"patient_id", patientId}, {"status", "in"}};
		admissionId=hibernate_manager.queryValuesFromDatabase(fieldsAndValues, "admission_id", "Admission", "admission");
		return admissionId;
	 }
	 
	 private Boolean isBedOccipued(int bed, int ward){
		 Boolean isOccupied=false;
			Object[][][] fieldsAndValues={{{"current_ward", ward}, {"current_bed", bed}},{{"status", "in"}}};
			String[][] classAndObjectNames={{"Bht", "bht"},{"Admission", "admission"}};
			String[] requiredObjectAndField={"admission", "admission_id"};
			String[][] joinObjectAndField={{"bht", "admission_id"},{"admission", "admission_id"}};
			List list=hibernate_manager.queryUsingJoin(classAndObjectNames, fieldsAndValues, requiredObjectAndField, joinObjectAndField);
			if(list.size()>0){
				isOccupied=true;
			}
			return isOccupied;
	 }
	 
	 private Boolean isBedOccupiedByCorrectPerson(int bed, int ward, int admission_id){
		 Boolean isOccupiedbyCorrectPerson=false;
			Object[][][] fieldsAndValues={{{"current_ward", ward}, {"current_bed", bed}},{{"status", "in"}}};
			String[][] classAndObjectNames={{"Bht", "bht"},{"Admission", "admission"}};
			String[] requiredObjectAndField={"admission", "admission_id"};
			String[][] joinObjectAndField={{"bht", "admission_id"},{"admission", "admission_id"}};
			List list=hibernate_manager.queryUsingJoin(classAndObjectNames, fieldsAndValues, requiredObjectAndField, joinObjectAndField);
			if(list.size()>0){
				for(int i=0; i<list.size(); i++){
					int value=(Integer)list.get(i);
					if(value==admission_id){
						isOccupiedbyCorrectPerson=true;
						break;
					}
				}
			}
			return isOccupiedbyCorrectPerson;
	 }
	 
	 public void transferToAnotherHospital() throws NumberFormatException, HL7Exception{
		 PV1 pv1=adtMsg.getPV1();
		 PL fromPlace=pv1.getPriorPatientLocation();
		 System.out.print(adtMsg.getPID().getPatientID().getIDNumber().getValue());
		 int admission_id=this.getAdmissionId();
		 String from_bed=fromPlace.getBed().getValue();
		 String from_ward=fromPlace.getRoom().getValue();
		 if(isBedOccupiedByCorrectPerson(Integer.parseInt(from_bed), Integer.parseInt(from_ward), admission_id)){
			this.transfer_to_another_hospital.setPatientId(Integer.parseInt(adtMsg.getPID().getPatientID().getIDNumber().getValue()));
			this.transfer_to_another_hospital.setAdmissionId(admission_id);
			this.transfer_to_another_hospital.setFromHospital(fromPlace.getFacility().getUniversalID().getValue());
			this.transfer_to_another_hospital.setFromWardNo(from_ward);
			this.transfer_to_another_hospital.setFromBedNo(from_bed);
			this.transfer_to_another_hospital.setToHospital(pv1.getAssignedPatientLocation().getFacility().getUniversalID().getValue());
			this.transfer_to_another_hospital.setTreatmentSuggested(adtMsg.getPV2().getPatientConditionCode().getAlternateText().getValue());
			this.transfer_to_another_hospital.setTransferringOfficerId(pv1.getConsultingDoctor(0).getIDNumber().getValue());
			this.transfer_to_another_hospital.setReason(adtMsg.getPV2().getTransferReason().getText().getValue());
			this.transfer_to_another_hospital.setAmbulanceRequestId(Integer.parseInt(adtMsg.getPV2().getModeOfArrivalCode().getAlternateText().getValue()));
			this.transfer_to_another_hospital.setStatus(adtMsg.getPV2().getPatientStatusCode().getValue());
			this.transfer_to_another_hospital.setTransferTime(pv1.getDischargeDateTime(0).getTime().getValueAsDate());
			Boolean successTransfer=hibernate_manager.saveObject(transfer_to_another_hospital);
			Boolean successAdmissionUpdate=hibernate_manager.updateAdmissionToDischargeorTransfer(admission_id, "out");
			if(successTransfer && successAdmissionUpdate){
				 exchange.setAdmission_id(admission_id);
				 exchange.setCurrent_bed(0);
				 exchange.setCurrent_ward(0);
				 exchange.setExchange_date(pv1.getDischargeDateTime(0).getTime().getValueAsDate());
				 exchange.setPatient_id(Integer.parseInt(adtMsg.getPID().getPatientID().getIDNumber().getValue()));
				 exchange.setPrevious_bed(Integer.parseInt(from_bed));
				 exchange.setPrevious_ward(Integer.parseInt(from_ward));
				 hibernate_manager.saveObject(exchange);
			 }
			 else{
				 HL7Exception ex=new HL7Exception("Error while transfering patient!");
				 throw ex;
			 }
		 }
		 else{
			 HL7Exception ex=new HL7Exception("Given patient does not occupy the given bed");
			 throw ex;
		 }
	 }
	 
	
	//OpenMRS	
}
